package com.cc.demo.utils;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义类加载器
 **/
@Getter
@Slf4j
public class MyClassLoader extends URLClassLoader {

    /**
     * 已加载的类集合
     */
    private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>();

    /**
     * 构造方法
     *
     * @param urls   地址
     * @param parent 父类加载器
     */
    public MyClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> findClass(String name) {
        // 从已加载的类集合中获取指定名称的类
        Class<?> clazz = loadedClasses.get(name);
        if (clazz != null) {
            return clazz;
        }
        try {
            // 调用父类的findClass方法加载指定名称的类
            clazz = super.findClass(name);
            // 将加载的类添加到已加载的类集合中
            loadedClasses.put(name, clazz);
            return clazz;
        } catch (ClassNotFoundException e) {
            log.error("找不到该类");
            return null;
        }
    }

    public void unload(String name) {
        // 从其父类加载器的加载器层次结构中移除该类加载器
        try {
            loadedClasses.remove(name);
            try {
                // 调用该类的destroy方法，回收资源
                Class<?> clazz = loadedClasses.get(name);
                Method destroy = clazz.getDeclaredMethod("destroy");
                destroy.invoke(clazz);
            } catch (Exception e) {
                // 表明该类没有destroy方法
            }
            close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void unloadAll() {
        // 从其父类加载器的加载器层次结构中移除该类加载器
        try {
            for (Map.Entry<String, Class<?>> entry : loadedClasses.entrySet()) {
                // 从已加载的类集合中移除该类
                String className = entry.getKey();
                loadedClasses.remove(className);
                try {
                    // 调用该类的destroy方法，回收资源
                    Class<?> clazz = entry.getValue();
                    Method destroy = clazz.getDeclaredMethod("destroy");
                    destroy.invoke(clazz);
                } catch (Exception e) {
                    // 表明该类没有destroy方法
                }
            }
            close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
